--effect = value * StatLogic:GetStatMod("ADD_MANA_REG_MOD_INT") + (StatLogic:GetNormalManaRegenFromSpi(spi, int + value, calcLevel) - StatLogic:GetNormalManaRegenFromSpi(spi, int, calcLevel)) * StatLogic:GetStatMod("ADD_MANA_REG_MOD_NORMAL_MANA_REG")
--Mana Regen From Int

-----------------
-- Addon Setup --
-----------------
local ECSNAME = "Enhanced Character Statistics"

EnhancedCharStats = LibStub("AceAddon-3.0"):NewAddon(ECSNAME, "AceConsole-3.0", "AceHook-3.0", "AceEvent-3.0")
local ecs = LibStub("AceAddon-3.0"):GetAddon(ECSNAME)
local ecsVersion = GetAddOnMetadata(ECSNAME, "Version")

--Other Libs
local SL = LibStub("LibStatLogic-1.1")
local L = LibStub("AceLocale-3.0"):GetLocale(ECSNAME)

ecs.optionsFrame = {}

---------------------
-- Local Variables --
---------------------
local _
local db

local _, pClass = UnitClass("player")
local _, pRace = UnitRace("player")

--localize Globals
local floor = floor
local format = format
local select = select
local strsplit = string.split
local tolower = string.lower

local GameTooltip = GameTooltip
local UnitStat = UnitStat
local UnitArmor = UnitArmor
local UnitManaMax = UnitManaMax
local UnitBuff = UnitBuff
local UnitDebuff = UnitDebuff
local GetSpellLink = GetSpellLink
local GetSpellInfo = GetSpellInfo
local GetTalentInfo = GetTalentInfo
local GetCombatRating = GetCombatRating
local UnitAttackPower = UnitAttackPower
local GetTalentTabInfo = GetTalentTabInfo
local GetNumTalentTabs = GetNumTalentTabs
local GetSpellPenetration = GetSpellPenetration
local GetCombatRatingBonus = GetCombatRatingBonus
local GetGlyphSocketInfo = GetGlyphSocketInfo
local GetNumGlyphSockets = GetNumGlyphSockets

--localize Constants
local GREEN_FONT = GREEN_FONT_COLOR_CODE -- Green
local HIGHLIGHT_FONT = HIGHLIGHT_FONT_COLOR_CODE -- White
local RED_FONT = RED_FONT_COLOR_CODE -- Red
local FONT_CLOSE = FONT_COLOR_CODE_CLOSE -- "|r"
local NORMAL_R = NORMAL_FONT_COLOR.r
local NORMAL_G = NORMAL_FONT_COLOR.g
local NORMAL_B = NORMAL_FONT_COLOR.b

--localize constants
local CR_PARRY = CR_PARRY
local CR_CRIT_SPELL = CR_CRIT_SPELL
local CR_HIT_SPELL = CR_HIT_SPELL
local CR_HIT_MELEE = CR_HIT_MELEE
local CR_HIT_RANGE = CR_HIT_RANGE
local CR_ARMOR_PEN = CR_ARMOR_PENETRATION
local HIT_RATING = COMBAT_RATING_NAME6
local RATING = RATING

--Magic Numbers

--Small functions
local function percent(arg)
	return format("%.2f%%",arg);
end

-- StatLogic GetStatMods Lists
local GetStatList = {"Str", "Agi", "Sta", "Int", "Spi", }
local GetModList = {
	["Str"]	  = {"ADD_CR_PARRY_MOD_STR", },
	["Agi"]	  = {"ADD_HEALING_MOD_AGI", },
	["Sta"]   = {"ADD_AP_MOD_STA", "ADD_SPELL_DMG_MOD_STA", },
	["Int"]   = {"ADD_AP_MOD_INT", "ADD_MANA_REG_MOD_INT", "ADD_RANGED_AP_MOD_INT", "ADD_ARMOR_MOD_INT", "ADD_SPELL_DMG_MOD_INT", "ADD_HEALING_MOD_INT", },
	["Spi"]   = {"ADD_SPELL_DMG_MOD_SPI", "ADD_HEALING_MOD_SPI", "ADD_SPELL_CRIT_RATING_MOD_SPI", },
	["AP"]    = {"ADD_SPELL_DMG_MOD_AP", },
	["Armor"] = {"ADD_AP_MOD_ARMOR", },
}

local GetTooltipText = {
	ADD_CR_PARRY_MOD_STR	= L["INC_PARRY_CHANCE"],
	ADD_AP_MOD_STA		= L["INC_ATTACK_POWER"],
	ADD_AP_MOD_INT		= L["INC_ATTACK_POWER"],
	ADD_AP_MOD_ARMOR	= L["INC_ATTACK_POWER"],
	ADD_SPELL_DMG_MOD_STA	= L["INC_SPELL_POWER"],
	ADD_SPELL_DMG_MOD_INT	= L["INC_SPELL_POWER"],
	ADD_SPELL_DMG_MOD_SPI	= L["INC_SPELL_POWER"],
	ADD_SPELL_DMG_MOD_AP	= L["INC_SPELL_POWER"],
	ADD_HEALING_MOD_AGI	= L["INC_HEALING_POWER"],
	ADD_HEALING_MOD_INT	= L["INC_HEALING_POWER"],
	ADD_HEALING_MOD_SPI	= L["INC_HEALING_POWER"],
	ADD_MANA_REG_MOD_INT	= L["INC_MANA_REGEN"],
	ADD_RANGED_AP_MOD_INT	= L["INC_RANGED_POWER"],
	ADD_ARMOR_MOD_INT	= L["INC_ARMOR"],
	ADD_SPELL_CRIT_RATING_MOD_SPI = L["INC_SPELL_CRIT"],
}

--------------------
-- Initialization --
--------------------
function EnhancedCharStats:OnInitialize()
	--Init Database
	local defaults = {
		profile = {
			general = {
				showStats = true,
				spellHit = true,
				meleeHit = true,
				rangeHit = true,
				emptySpace = true,
			},
			stats = {
				dodge = true,
				replenish = true,
				intreplenish = true,
			},
			hitrating = {
				hitDebuff = false,
				heroicOn = false,
				armorPen = true,
				spellPen = true,
				armorPenLong = false,
				spellPenLong = false,
			},
			GetModList = {
				["ADD_CR_PARRY_MOD_STR"] = true,
				["ADD_AP_MOD_STA"] = true,
				["ADD_SPELL_DMG_MOD_STA"] = true,
				["ADD_AP_MOD_INT"] = true,
				["ADD_MANA_REG_MOD_INT"] = true,
				["ADD_RANGED_AP_MOD_INT"] = true,
				["ADD_ARMOR_MOD_INT"] = true,
				["ADD_SPELL_DMG_MOD_INT"] = true,
				["ADD_SPELL_DMG_MOD_SPI"] = true,
				["ADD_SPELL_DMG_MOD_AP"] = true,
				["ADD_AP_MOD_ARMOR"] = true,
				["ADD_SPELL_CRIT_RATING_MOD_SPI"] = true,
				["ADD_HEALING_MOD_AGI"] = false,
				["ADD_HEALING_MOD_STA"] = false,
				["ADD_HEALING_MOD_INT"] = false,
				["ADD_HEALING_MOD_SPI"] = false,
			},
		},
	}
	if (pClass == "DRUID") or (pClass == "SHAMAN") then
		defaults.profile.GetModList["ADD_HEALING_MOD_INT"] = true;
		defaults.profile.GetModList["ADD_HEALING_MOD_SPI"] = true;
	end
	if (pClass == "ROGUE") or (pClass == "WARRIOR") or (pClass == "DEATHKNIGHT") then
		defaults.profile.stats.replenish = false;
		defaults.profile.stats.intreplenish = false;
	end
	ecs.db = LibStub("AceDB-3.0"):New("EnhancedCharStatsDB",defaults);
	db = ecs.db.profile
	--Init Options
	self:SetupOptions(db);
	self:RegisterChatCommand("ecs", "ChatCommand");
	self:RegisterChatCommand("enhancedcharstats", "ChatCommand");
end

function ecs:OnEnable()
	ecs:SecureHook("PaperDollFrame_SetStat");
	ecs:SecureHook("PaperDollFrame_SetArmor");
	ecs:SecureHook("PaperDollFrame_SetAttackPower");
	ecs:SecureHook("PaperDollFrame_SetManaRegen");
	ecs:SecureHook("UpdatePaperdollStats");
	ecs:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED");
end

function ecs:OnDisable()
	ecs:UnhookAll();
end

function ecs:ChatCommand(input)
	InterfaceOptionsFrame_OpenToCategory(self.optionsFrame["About"])
end

function ecs:ACTIVE_TALENT_GROUP_CHANGED(...)
	ecs:ScanHitTalents(arg1);
end

function ecs:AddTooltipLine(statFrame, text)
	if (not statFrame.tooltip2) then
		statFrame.tooltip2 = text
	else 
		statFrame.tooltip2 = statFrame.tooltip2.."\n"..text
	end
end

---------------
-- Mods Scan --
---------------
function ecs:ScanTooltipMods(statFrame, Stat, Mod)
	emptySpace = false
	local total
	for n=1, #GetModList[Mod] do
		local StatMod, SLMod = GetModList[Mod][n], SL:GetStatMod(GetModList[Mod][n])
		-- If there is any conversions to do.
		if (db.GetModList[StatMod]) and (SLMod > 0) then
			total = floor((Stat*SLMod)+0.5);
			--If conversion is a Rating, convert into percent chance
			if (StatMod == "ADD_CR_PARRY_MOD_STR") then
				total = percent(SL:GetEffectFromRating(total, CR_PARRY));
			end
			if (StatMod == "ADD_SPELL_CRIT_RATING_MOD_SPI") then
				total = percent(SL:GetEffectFromRating(total, CR_CRIT_SPELL));
			end
			--Insert new data into the tooltip text, insert empty space if possible.
			if (db.general.emptySpace) and (not emptySpace) then
				ecs:AddTooltipLine(statFrame, "");
				emptySpace = true;
			end
			ecs:AddTooltipLine(statFrame, GetTooltipText[StatMod]..total);
		end
	end
end

---------------
-- Hit Scans --
---------------
local HitTalentScan = false
local SpecHit = { ["melee"] = { 0,0,0, },  ["range"] = { 0,0,0, }, ["spell"] = { 0,0,0, }, }
local TalentHit = { ["melee"] = { 0,0,0, },  ["range"] = { 0,0,0, }, ["spell"] = { 0,0,0, }, }
--ToDo: Make SpecHit works for Melee, Ranged and Spell Hit.
--AddHit(Spell,0,0,1) for example. SpecHit[spell][1-3]
--Spell: [1-2-3] Talent Trees
--Melee: [1] OneHanded [2] TwoHanded [3] DualWield
--Range [1-2-3] Ranged

-- Add specific hit chance modifier
function ecs:AddHit(school,One,Two,Three)
	SpecHit[school][1] = SpecHit[school][1] + One
	SpecHit[school][2] = SpecHit[school][2] + Two
	SpecHit[school][3] = SpecHit[school][3] + Three
end
-- Add generic hit chance modifier
function ecs:AddAllHit(school,value)
	if (school == "all") then
		for n=1,3 do SpecHit["melee"][n] = SpecHit["melee"][n] + value end
		for n=1,3 do SpecHit["range"][n] = SpecHit["range"][n] + value end
		for n=1,3 do SpecHit["spell"][n] = SpecHit["spell"][n] + value end
	else
		for n=1,3 do SpecHit[school][n] = SpecHit[school][n] + value end
	end
end
function ecs:ScanHitTalents(tGroup)
	-- Scanning Talents
	SpecHit = { 
		["melee"] = { 0,0,0, },  
		["range"] = { 0,0,0, }, 
		["spell"] = { 0,0,0, }, 
	}
	local BonusHit;
	if ( pClass == "DEATHKNIGHT" ) then
		-- Nerves of Cold Steel - +1/2/3% OneHanded/DW Hit
		BonusHit = select(5, GetTalentInfo(2,6));
		if (BonusHit > 0) then
			ecs:AddHit("melee",BonusHit,0,BonusHit);
		end
		-- Virulance - +1/2/3% Spell Hit
		BonusHit = select(5, GetTalentInfo(3,2));
		if (BonusHit > 0) then
			ecs:AddAllHit("spell",BonusHit);
		end
	end
	if ( pClass == "DRUID" ) then
		-- Balance of Power - +2/4% Spell Hit
		BonusHit = select(5, GetTalentInfo(1,17));
		if (BonusHit > 0) then
			ecs:AddAllHit("spell",BonusHit*2);
		end
	end
	if ( pClass == "HUNTER" ) then
		-- Focused Aim - +1/2/3% Ranged Hit
		BonusHit = select(5, GetTalentInfo(2,3));
		if (BonusHit > 0) then
			ecs:AddAllHit("range",BonusHit);
		end
	end
	if ( pClass == "MAGE" ) then
		-- Arcane Focus - +1/2/3% Arcane Spell Hit
		BonusHit = select(5, GetTalentInfo(1,2,nil,nil,tGroup));
		if (BonusHit > 0) then
			ecs:AddHit("spell",BonusHit,0,0);
		end
		-- Precision - +1/2/3% Spell Hit
		BonusHit = select(5, GetTalentInfo(3,6,nil,nil,tGroup));
		if (BonusHit > 0) then
			ecs:Print("You has Precision");
			ecs:AddAllHit("spell",BonusHit);
		end
	end
	if ( pClass == "PRIEST" ) then
		-- Shadow Focus - 1/2/3% Shadow Spell Hit
		BonusHit = select(5, GetTalentInfo(3,6));
		if (BonusHit > 0) then
			ecs:AddHit("spell",0,0,BonusHit);
		end
	end
	if ( pClass == "ROGUE" ) then
		-- Precision - 1/2/3/4/5% Spell Hit And Melee Hit
		BonusHit = select(5, GetTalentInfo(2,6));
		if (BonusHit > 0) then
			ecs:AddAllHit("all",BonusHit);
		end
	end
	if ( pClass == "SHAMAN" ) then
		-- Elemental Precision - 1/2/3% Elemental Spell Hit
		BonusHit = select(5, GetTalentInfo(1,14));
		if (BonusHit > 0) then
			ecs:AddHit("spell",BonusHit,0,0);
		end
		-- Dual Wield Specialization - 2/4/6% DualWield Hit
		BonusHit = select(5, GetTalentInfo(2,19));
		if (BonusHit > 0) then
			ecs:AddHit("melee",0,0,BonusHit*2);
		end
	end
	if ( pClass == "WARLOCK" ) then
		-- Suppression - 1/2/3% Spell Hit
		BonusHit = select(5, GetTalentInfo(1,2));
		if (BonusHit > 0) then
			ecs:AddAllHit("spell",BonusHit);
		end
	end
	if ( pClass == "WARRIOR" ) then
		-- Precision - 1/2/3% Melee Hit
		BonusHit = select(5, GetTalentInfo(2,13));
		if (BonusHit > 0) then
			ecs:AddAllHit("melee",BonusHit);
		end
	end
	TalentHit["melee"] = { SpecHit["melee"][1], SpecHit["melee"][2], SpecHit["melee"][3] };
	TalentHit["range"] = { SpecHit["range"][1], SpecHit["range"][2], SpecHit["range"][3] };
	TalentHit["spell"] = { SpecHit["spell"][1], SpecHit["spell"][2], SpecHit["spell"][3] };
	HitTalentScan = true;
end

function ecs:ScanHitBonuses()
	--Check for Buffs
	--Heroic Presence
	SpecHit["melee"] = { TalentHit["melee"][1], TalentHit["melee"][2], TalentHit["melee"][3] };
	SpecHit["range"] = { TalentHit["range"][1], TalentHit["range"][2], TalentHit["range"][3] };
	SpecHit["spell"] = { TalentHit["spell"][1], TalentHit["spell"][2], TalentHit["spell"][3] };
	if (db.hitrating.heroicOn) then
		ecs:AddAllHit("all",1);
	elseif (UnitBuff("player", GetSpellInfo(6562))) then
		ecs:AddAllHit("all",1);
	elseif (UnitBuff("player", GetSpellInfo(28878))) then
		ecs:AddAllHit("all",1);
	end
	--Check for Debuffs
	--Misery Ranks
	if (db.hitrating.hitDebuff) then
		ecs:AddAllHit("spell",3);
	elseif (UnitDebuff("target", GetSpellInfo(33198))) then
		ecs:AddAllHit("spell",3);
	elseif (UnitDebuff("target", GetSpellInfo(33197))) then
		ecs:AddAllHit("spell",2);
	elseif (UnitDebuff("target", GetSpellInfo(33196))) then
		ecs:AddAllHit("spell",1);
	end
end

---------------
-- API Hooks --
---------------
function ecs:PaperDollFrame_SetArmor(statFrame, unit)
	if (db.general.showStats) then
		ecs:ScanTooltipMods(statFrame, UnitArmor("player"), "Armor");
	end
end

function ecs:PaperDollFrame_SetAttackPower(statFrame, unit)
	if (db.general.showStats) then
		ecs:ScanTooltipMods(statFrame, UnitAttackPower("player"), "AP");
	end
end

function ecs:PaperDollFrame_SetManaRegen(statFrame)
	if (db.general.showStats) and (db.stats.replenish) then
		local replenish = floor((UnitManaMax("player") * 0.0125) + 0.5)
		ecs:AddTooltipLine(statFrame, replenish..L["REPLENISH"]);
	end
end

function ecs:PaperDollFrame_SetStat(statFrame, statIndex)
	if (db.general.showStats) then
		local _, statNum = UnitStat("player",statIndex);
		ecs:ScanTooltipMods(statFrame, statNum, GetStatList[statIndex]);
		if (db.stats.dodge) and (statIndex == 2) then
			local ModAgi = percent(SL:GetDodgeFromAgi(statNum));
			ecs:AddTooltipLine(statFrame,L["INC_DODGE"]..ModAgi);
		end
		if (db.stats.intreplenish) and (statIndex == 4) then
			local ModInt = floor((statNum * 15 * 0.0125) + 0.5)
			ecs:AddTooltipLine(statFrame,L["INC_REPLENISH"]..ModInt);
		end
		statFrame:Show();
	end
end

function ecs:UpdatePaperdollStats(prefix, index)
	local stat3 = getglobal(prefix..3);
	local stat4 = getglobal(prefix..4);
	stat3:SetScript("OnEnter",PaperDollStatTooltip);
	stat4:SetScript("OnEnter",PaperDollStatTooltip);
	if ( index == "PLAYERSTAT_MELEE_COMBAT" ) then
		if (db.general.meleeHit) then
			stat4:SetScript("OnEnter", ecs_MeleeHit);
		end
	end
	if ( index == "PLAYERSTAT_SPELL_COMBAT" ) then
		if (db.general.spellHit) then
			stat3:SetScript("OnEnter", ecs_SpellHit);
		end
		stat4:SetScript("OnEnter", CharacterSpellCritChance_OnEnter);
	end
end

-- Spell Hit Tooltip
function ecs_SpellHit(self)
	if not (HitTalentScan) then ecs:ScanHitTalents(); end
	ecs:ScanHitBonuses();
	GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
	local spellHitBonus = GetCombatRatingBonus(CR_HIT_SPELL);
	GameTooltip:SetText(HIGHLIGHT_FONT..HIT_RATING.." "..GetCombatRating(CR_HIT_SPELL)..FONT_CLOSE);
	GameTooltip:AddDoubleLine(RATING,percent(spellHitBonus));
	local spellHit;
	for i=1, GetNumTalentTabs() do
		TalentTabName, TalentTabTexture = GetTalentTabInfo(i);
		spellHit = percent(spellHitBonus + SpecHit["spell"][i]);
		GameTooltip:AddDoubleLine(TalentTabName, spellHit);
		GameTooltip:AddTexture(TalentTabTexture);
	end
	if (db.hitrating.spellPen) then
		if (db.hitrating.spellPenLong) then
			GameTooltip:AddLine(format(L["SPELL_PEN_LONG"],GetSpellPenetration(),GetSpellPenetration()), NORMAL_R, NORMAL_G, NORMAL_B,1);
		else
			GameTooltip:AddLine(L["SPELL_PEN"]..GetSpellPenetration(),NORMAL_R,NORMAL_G,NORMAL_B,1);
		end
	end
	GameTooltip:Show();
end

-- Melee Hit Tooltip
local NumMeleeTabs = { "OneHanded", "TwoHanded", "DualWield" }
local MeleeTabTexture = { 20196, 55107, 30798 }
-- MeleeTabTexture { One-Handed Weapon Spec, Two-Handed Weapon Spec, Dual Wield }
function ecs_MeleeHit(self)
	if not (HitTalentScan) then ecs:ScanHitTalents(); end
	ecs:ScanHitBonuses();
	GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
	local meleeHitBonus = GetCombatRatingBonus(CR_HIT_MELEE);
	GameTooltip:SetText(HIGHLIGHT_FONT..HIT_RATING.." "..GetCombatRating(CR_HIT_MELEE)..FONT_CLOSE);
	GameTooltip:AddDoubleLine(RATING,percent(meleeHitBonus));
	local meleeHit;
	for i=1, #NumMeleeTabs do
		meleeHit = percent(meleeHitBonus + SpecHit["melee"][i]);
		GameTooltip:AddDoubleLine(NumMeleeTabs[i], meleeHit);
		local _, _, MeleeTexture = GetSpellInfo(MeleeTabTexture[i]);
		GameTooltip:AddTexture(MeleeTexture);
	end
	if (db.hitrating.armorPen) then
		if (db.hitrating.armorPenLong) then
			GameTooltip:AddLine(format(L["ARMOR_PEN_LONG"],GetCombatRating(CR_ARMOR_PEN),GetArmorPenetration()),NORMAL_R,NORMAL_G,NORMAL_B,1);
		else
			GameTooltip:AddDoubleLine(L["ARMOR_PEN"]..GetCombatRating(CR_ARMOR_PEN), percent(GetArmorPenetration()));
		end
	end
	GameTooltip:Show();
end